package com.gis.service.impl;

import java.io.File;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.annotation.Resource;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gis.entity.LayerData;
import com.gis.enums.ApiErrorCode;
import com.gis.exception.ApiException;
import com.gis.mapper.LayerMapper;
import org.springframework.http.HttpMethod;
import com.gis.service.LayerService;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class LayerServiceImpl extends ServiceImpl<LayerMapper, LayerData> implements LayerService {

    @Value("${files.dir}/${files.relative-save-dir}/${files.netcdf}")
    private String netcdfDir;

    @Value("${files.dir}/${files.relative-save-dir}/${files.png}")
    private String pngDir;

    @Value("${files.dir}/${files.relative-save-dir}/${files.jpg}")
    private String jpgDir;

    @Value("${gis.geoserver.workspace}")
    private String workSpaceName;

    @Value("${gis.geoserver.username}")
    private String username;

    @Value("${gis.geoserver.password}")
    private String password;

    @Value("${gis.geoserver.geoserver_url}")
    private String geoserverUrl;

    @Resource
    private RestTemplate restTemplate;

    @Override
    public LayerData uploadFile(LayerData layerData, MultipartFile file) throws Exception {
        try {
            
            // 如果已经存在，则抛出异常
            if (this.query().eq("origin_name", layerData.getOriginName()).one() != null) {
                throw new ApiException(ApiErrorCode.FILE_EXISTS);
            }

            // 获取文件信息
            String originalFilename = file.getOriginalFilename();
            String type = originalFilename.substring(originalFilename.lastIndexOf(".") + 1);
            Long size = file.getSize();

            // 日志记录
            log.info("originalFilename: " + originalFilename);
            log.info("type: " + type);
            log.info("size: " + size);
            // 保存文件
            String uuid = UUID.randomUUID().toString();
            String savedFileName = uuid + "_" + originalFilename;
            String savedDirName = uuid + "_" + originalFilename.substring(0, originalFilename.lastIndexOf("."));
            String dataLevel = this.setDataLevelWhenUploadFile(type);
            File destDir = this.createDir(savedDirName, type);
            // 判断文件路径是否为存在
            if (!destDir.exists()) {
                destDir.mkdirs();
            }

            File uploadFile = new File(destDir + "/" + savedFileName);
            file.transferTo(uploadFile);

            layerData.setOriginName(originalFilename);
            layerData.setSavedName(savedFileName);
            layerData.setSavedDir(destDir.getAbsolutePath());
            layerData.setFileType(type);
            // 数据是png和
            if (layerData.getDataLevel() == null || layerData.getDataLevel().isEmpty()) {
                layerData.setDataLevel(dataLevel);
            }

            // 设置发布状态为false
            // 发布状态为false表示未发布
            // 如果未设置发布状态，则默认为false
            if (layerData.getIsPublished() == null || layerData.getIsPublished() == false) {
                layerData.setIsPublished(false);
            }

            this.save(layerData);

            return layerData;
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
    }

    private String setDataLevelWhenUploadFile(String type) {
        String L1B = "L1B";
        switch (type) {
            case "png":
                return L1B;
            case "jpg":
                return L1B;
            case "jpeg":
                return L1B;
            default:
                return "";
        }
    }

    /**
     * 添加数据存储到GeoServer
     * 
     * @param storeName        存储名称
     * @param type             存储类型
     * @param originalFilename 原始文件名
     */
    @Override
    public HashMap<String, Object> addStore(String storeName, String type, String originalFilename) throws Exception {
        HashMap<String, Object> result = new HashMap<>();
        try {
            QueryWrapper<LayerData> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("origin_name", originalFilename);
            LayerData queryResult = this.getOne(queryWrapper);

            String FileDir = queryResult.getSavedDir() + "/" + queryResult.getSavedName();
            String FileUrl = "file://" + FileDir;
            String workspaceName = this.workSpaceName;
            // String storeName = originalFilename.substring(0,
            // originalFilename.lastIndexOf("."));

            // 设置请求头
            HttpHeaders requestHeaders = this.getGeoserverRequestHeader();

            // 设置请求体
            Map<String, Object> requestBoody = new HashMap<>();
            requestBoody.put("coverageStore", new HashMap<String, Object>() {
                {
                    put("name", storeName);
                    put("url", FileUrl);
                    put("type", type);
                    put("enabled", true);
                    put("workspace", new HashMap<String, Object>() {
                        {
                            put("name", workspaceName);
                        }
                    });
                    put("__default__", false);
                }
            });

            HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(requestBoody, requestHeaders);

            // 发送请求
            String url = geoserverUrl + "/rest/workspaces/" + workspaceName + "/coveragestores";

            // 查看返回值；201表示创建成功
            String response = restTemplate.postForObject(url, requestEntity, String.class);
            log.info("response: " + response);
            return result;

        } catch (Exception e) {
            e.printStackTrace();
            result.put("error", e.getMessage());
            result.put("message", "添加失败");
            throw new Exception("添加数据存储失败: " + e.getMessage());
        }
    }

    /**
     * 添加图层到GeoServer
     * 
     * @param storeName  存储名称
     * @param layerName  图层名称
     * @param nativeName 原始名称
     * @param originName 原始文件名
     */
    @Override
    public HashMap<String, Object> addLayer(String storeName, String layerName, String nativeName, String originName)
            throws Exception {
        // TODO Auto-generated method stub
        HashMap<String, Object> result = new HashMap<>();

        try {
            String workspaceName = this.workSpaceName;
            String url = geoserverUrl + "/rest/workspaces/" + workspaceName + "/coveragestores/" + storeName
                    + "/coverages";

            // 设置请求头
            HttpHeaders requestHeaders = this.getGeoserverRequestHeader();

            // 设置请求体
            Map<String, Object> requestBoody = new HashMap<>();
            requestBoody.put("coverage", new HashMap<String, Object>() {
                {
                    put("name", layerName);
                    put("nativeName", nativeName);
                    put("title", layerName);
                    put("enable", true);
                }
            });

            // 整合请求
            HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(requestBoody, requestHeaders);

            // 发送请求
            String response = restTemplate.postForObject(url, requestEntity, String.class);

            // 查看返回值；201表示创建成功
            log.info("response: " + response);
            // 请求范围值
            log.info("请求范围值");
            url = geoserverUrl + "/rest/workspaces/" + workspaceName + "/coverages/" + layerName;
            log.info("请求url: " + url);

            HashMap<String, Object> box = this.GetRangeFromGeoserver(url);
            QueryWrapper<LayerData> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("origin_name", originName);
            LayerData queryResult = this.getOne(queryWrapper);
            queryResult.setMinX((Float) box.get("minX"));
            queryResult.setMaxX((Float) box.get("maxX"));
            queryResult.setMinY((Float) box.get("minY"));
            queryResult.setMaxY((Float) box.get("maxY"));
            this.update(queryResult, queryWrapper);

            result.put("response", response);

        } catch (Exception e) {
            e.printStackTrace();
            result.put("error", e.getMessage());
            result.put("message", "添加失败");
            throw e;
        }

        return result;
    }

    /**
     * 修改图层样式
     * 
     * @param layerName 图层名称
     * @param styleName 样式名称
     * @return HashMap<String, Object> 返回结果
     */
    @Override
    public HashMap<String, Object> modifyLayer(String layerName, String styleName) throws Exception {
        HashMap<String, Object> result = new HashMap<>();

        try {
            String workspaceName = this.workSpaceName;
            ;
            String url = geoserverUrl + "/rest/layers/" + workspaceName + ":" + layerName;

            // 设置请求头
            HttpHeaders requestHeaders = this.getGeoserverRequestHeader();

            // 设置请求体
            Map<String, Object> requestBoody = new HashMap<>();
            requestBoody.put("layer", new HashMap<String, Object>() {
                {
                    put("defaultStyle", new HashMap<String, Object>() {
                        {
                            put("name", styleName);
                        }
                    });
                }
            });

            // 整合请求
            HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(requestBoody, requestHeaders);

            // 发送请求
            String response = restTemplate
                    .exchange(url, org.springframework.http.HttpMethod.PUT, requestEntity, String.class).getBody();

            // 查看返回值；201表示创建成功
            log.info("response: " + response);
            result.put("response", response);
            // 更新图层样式
            QueryWrapper<LayerData> queryWrapper = new QueryWrapper<>();
            queryWrapper.likeRight("origin_name", layerName).eq("is_published", true);
            LayerData queryResult = this.getOne(queryWrapper);
            queryResult.setStyle(styleName);
            this.updateById(queryResult);

        } catch (NullPointerException e) {
            e.printStackTrace();
            result.put("error", e.getMessage());
            result.put("message", "添加失败");
            throw e;
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }

        return result;
    }

    @Override
    public LayerData publishLayer(LayerData layerData, MultipartFile file, String type, String storeName,
            String layerName, String nativeName, String styleName) throws Exception {
        try {

            // 添加store
            this.addStore(storeName, type, layerData.getOriginName());

            // 添加layer
            this.addLayer(storeName, layerName, nativeName, layerData.getOriginName());

            // 修改layer
            this.modifyLayer(layerName, styleName);

            // 上传文件
            // 添加store和addlayer后，状态设置为已发布
            layerData.setIsPublished(true);
            this.updateById(layerData);
            this.uploadFile(layerData, file);
            return layerData;
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
    }

    @Override
    @Transactional
    public LayerData publishLayer(MultipartFile file, String type, String style) throws Exception {
        String originFileName = file.getOriginalFilename();

        // REG_H8_AHI_201510060800_02km_SST_L2B_SIO_V01
        try {
            LayerData layerData = this.fileNameAnalyse(originFileName);
            String variableName = layerData.getVariableName();

            String storeName = originFileName.substring(0, originFileName.lastIndexOf("."));
            String layerName = storeName;
            String styleName = style;

            // 2025-5-20 修改，先不发布图层，上传后再手动发布
            // return this.publishLayer(layerData, file, type, storeName, layerName,
            // variableName, styleName);
            layerData.setIsPublished(false);
            LayerData result = this.uploadFile(layerData, file);
            return result;

        } catch (Exception e) {
            e.printStackTrace();

            throw new Exception("文件名格式错误");
        }

    }

    @Override
    public HashMap<String, Object> deleteLayer(String storeName, String layerName) throws Exception {
        // TODO Auto-generated method stub
        HashMap<String, Object> result = new HashMap<>();
        try {
            String workspaceName = this.workSpaceName;
            ;
            String url = geoserverUrl + "/rest/workspaces/" + workspaceName + "/coveragestores/" + storeName
                    + "/coverages/" + layerName;

            // 设置请求头
            HttpHeaders requestHeaders = this.getGeoserverRequestHeader();

            // 整合请求
            HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(null, requestHeaders);

            // 发送请求
            String response = restTemplate.exchange(url, HttpMethod.DELETE, requestEntity, String.class).getBody();

            // 查看返回值；201表删除成功
            log.info("response: " + response);
            result.put("response", response);

            return result;

        } catch (Exception e) {
            e.printStackTrace();
            result.put("error", e.getMessage());
            result.put("message", "删除失败");
            throw e;
        }
    }

    @Override
    public HashMap<String, Object> deleteStore(String storeName) throws Exception {
        HashMap<String, Object> result = new HashMap<>();
        try {
            String workspaceName = this.workSpaceName;
            ;
            String url = geoserverUrl + "/rest/workspaces/" + workspaceName + "/coveragestores/" + storeName
                    + "?recurse=true";

            // 设置请求头
            HttpHeaders requestHeaders = this.getGeoserverRequestHeader();

            // 整合请求
            HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(null, requestHeaders);

            // 发送请求
            String response = restTemplate.exchange(url, HttpMethod.DELETE, requestEntity, String.class).getBody();
            QueryWrapper<LayerData> queryWrapper = new QueryWrapper<>();
            queryWrapper.likeRight("origin_name", storeName);
            LayerData layerData = this.getOne(queryWrapper, true);
            String savedDir = layerData.getSavedDir();
            this.remove(queryWrapper);
            // 删除本地文件夹及其文件
            File file = new File(savedDir);
            log.info("file: " + savedDir);
            if (file.exists()) {
                FileUtils.deleteDirectory(file);
            }

            // 查看返回值；20X表删除成功
            log.info("response: " + response);
            result.put("response", response);

            return result;

        } catch (Exception e) {
            e.printStackTrace();
            result.put("error", e.getMessage());
            result.put("message", "删除失败");
            throw e;
        }
    }

    @Override
    public HashMap<String, Object> GetRangeFromGeoserver(String url) throws Exception {
        HashMap<String, Object> result = new HashMap<>();
        try {
            // 设置请求头
            HttpHeaders requestHeaders = getGeoserverRequestHeader();

            // 整合请求
            HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(null, requestHeaders);

            // 发送请求
            String response = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class).getBody();

            JSONObject coverageObject = JSONObject.parseObject(response).getJSONObject("coverage");
            JSONObject nativeBoundingBox = coverageObject.getJSONObject("nativeBoundingBox");
            result.put("minX", nativeBoundingBox.getFloat("minx"));
            result.put("maxX", nativeBoundingBox.getFloat("maxx"));
            result.put("minY", nativeBoundingBox.getFloat("miny"));
            result.put("maxY", nativeBoundingBox.getFloat("maxy"));
            return result;

        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
    }

    @Override
    public LayerData publishLayer(MultipartFile file) throws Exception {
        String fileName = file.getOriginalFilename();
        String type = fileName.substring(fileName.lastIndexOf(".") + 1);
        String variableName;
        try {
            variableName = fileName.split("_")[5].toLowerCase();
        } catch (Exception e) {
            throw new Exception("文件名格式错误，正确文件名格式：REG_H8_AHI_201510060800_02km_SST_L2B_SIO_V01.nc");
        }
        String styleName = "Normalize-0-1-Style";
        String fileType = "NetCDF";
        switch (type) {
            case "nc":
                fileType = "NetCDF";
                break;
            case "png":
                fileType = "PNG";
                break;
            case "tif":
                fileType = "GeoTIFF";
                break;
            default:
                break;
        }
        switch (variableName) {
            case "cld":
                styleName = "cld-style";
                break;
            case "sst":
                styleName = "sst-style";
                break;
            case "fog":
                styleName = "fog-style";
                break;
            default:
                break;
        }

        return this.publishLayer(file, fileType, styleName);
    }

    /**
     * 根据文件id获取文件
     * 
     * @param id 文件id
     * @return HashMap<String, Object> 文件对象
     */
    @Override
    public org.springframework.core.io.Resource getFile(Integer id) throws Exception {
        QueryWrapper<LayerData> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", id);
        LayerData queryResult = this.getOne(queryWrapper);
        File file = new File(queryResult.getSavedDir() + "/" + queryResult.getSavedName());
        if (file.exists()) {
            org.springframework.core.io.Resource resource = new org.springframework.core.io.FileSystemResource(file);
            return resource;
        } else {
            throw new Exception("文件不存在");
        }
    }

    /**
     * 获取文件并让geoserver发布
     * 
     * @param id 文件id
     * @return Boolean 是否发布成功
     */
    @Override
    public Boolean layerPublish(LayerData layerData) throws Exception {
        if (layerData == null) {
            throw new Exception("文件不存在");
        }
        // 存储空间(store name)和图层名称(layer name)与原始文件名(去后缀)相同
        String storeName = layerData.getOriginName().substring(0, layerData.getOriginName().lastIndexOf("."));
        String fileType = layerData.getFileType();
        String originNameFileName = layerData.getOriginName();
        String styleName = "Normalize-0-1-Style";
        switch (fileType) {
            case "nc":
                fileType = "NetCDF";
                break;
            default:
                break;
        }
        try {
            this.addStore(storeName, fileType, originNameFileName);
            this.addLayer(storeName, storeName, layerData.getVariableName(), layerData.getOriginName());
            switch (layerData.getVariableName()) {
                case "cld":
                    styleName = "cld-style";
                    break;
                case "sst":
                    styleName = "sst-style";
                    break;
                case "fog":
                    styleName = "fog-style";
                    break;
                default:
                    break;
            }
            layerData.setIsPublished(true);
            this.updateById(layerData);
            this.modifyLayer(storeName, styleName);
        } catch (Exception e) {
            throw e;
            // return false;
        }
        // 图层名称与存储空间名称相同
        return true;
    }

    /**
     * 根据上传的文件名解析出图层数据
     * 
     * @param originFileName 原始文件名
     * @return LayerData 图层数据对象
     * @throws Exception 如果文件名格式不正确则抛出异常
     */
    @Override
    public LayerData fileNameAnalyse(String originFileName) throws Exception {
        String fileNameWithoutExt = originFileName.substring(0, originFileName.lastIndexOf(".") - 1);
        log.info("上传文件名：" + originFileName);
        ArrayList<String> properties = new ArrayList<>(Arrays.asList(fileNameWithoutExt.split("_")));
        String satellite = properties.get(1);
        String sensor = properties.get(2);
        String dateString = properties.get(3);
        String resolution = properties.get(4);
        String variableName = properties.get(5).toLowerCase();
        String dataLevel = properties.get(6);
        LayerData layerData = new LayerData();
        layerData.setSatellite(satellite);
        layerData.setSensor(sensor);
        layerData.setYear(Integer.parseInt(dateString.substring(0, 4)));
        layerData.setMonth(Integer.parseInt(dateString.substring(4, 6)));
        layerData.setDay(Integer.parseInt(dateString.substring(6, 8)));

        // 字符串转为时间戳
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmm");
        LocalDateTime dateTime = LocalDateTime.parse(dateString, formatter);
        Timestamp timestamp = Timestamp.valueOf(dateTime);
        log.info("时间戳：" + timestamp);
        layerData.setTime(timestamp);
        layerData.setResolution(resolution);
        layerData.setVariableName(variableName);
        layerData.setDataLevel(dataLevel);
        return layerData;
    }

    /**
     * 生成默认的GeoServer请求头，所有访问GeoServer的请求都需要这个头
     * 
     * @return HttpHeaders 返回请求头
     */
    private HttpHeaders getGeoserverRequestHeader() {
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.add("Content-type", "application/json");
        requestHeaders.setBasicAuth(this.username, this.password);
        return requestHeaders;
    }

    @Override
    public Set<String> fieldQuery(String fieldName) {
        return this.baseMapper.fieldQuery(fieldName);
    }

    private File createDir(String savedDirName, String type) throws Exception {
        File destDir;
        switch (type) {
            case "nc":
                destDir = new File(netcdfDir + "/" + savedDirName);
                break;
            case "png":
                destDir = new File(pngDir + "/" + savedDirName);
                break;
            case "jpg":
                destDir = new File(pngDir + "/" + savedDirName);
                break;
            case "jpeg":
                destDir = new File(pngDir + "/" + savedDirName);
                break;
            default:
                throw new Exception("不支持的文件类型：" + type);
        }
        return destDir;
    }

}
